from helpFunc import *
import socket,select,os,time
from connbase import *
from ser import *
import gitPull,threading
from datetime import datetime
class tcpRePeer(tcpReBase):
    def __init__(self,dataPort,numPort,maxConn,cachePerConn,ss,bufSize):
        self.dataPort = dataPort
        self.numPort = numPort    
        tcpReBase.__init__(self,dataPort,numPort,maxConn,cachePerConn,bufSize)     
        self.connectPollingMap = {}
        self.ssPort = ss
        self.fileTimes = {}
        self.udpMap = {}
        self.udpSockMap = {}
        
    def delUdpMap(self):
        l = list(self.udpMap.keys())
        for i in l:
            s = self.udpMap[i]['sock']
            t = self.udpMap[i]['time']
            if time.monotonic()-t>udpReTime:
                del self.udpSockMap[s]
                del self.udpMap[i]
                s.close()
                
    def gitC(self):
        self.gitT = t = threading.Thread(target=gitPull.a)
        t.setDaemon(True)
        t.start()          

    def sendFile(self):
        try:
            runningPath = os.path.split(os.path.realpath(__file__))[0]
            p = runningPath + '/file.txt'
            if not os.path.exists(p):
                return
            xx = int(os.path.getmtime(p))
            if xx in self.fileTimes:
                return
            if time.time()-xx>30:
                return
            self.fileTimes[xx] = 0
            for k in sorted(list(self.fileTimes.keys())):
                if xx-k>100 and len(self.fileTimes)>1:
                    del self.fileTimes[k]
            f = open(p,'rb')
            s = f.read()
            f.close()
            dd = self.makeMsg(file_g,b'a'*16,s)    
            self.sendMsg(dd)      
        except:
            pass

    def start(self,onePakcLen,totalSendLen,
              packEffectTime,timeoutInter,lp,k):
        import threading
        def ww():
            try:
                a = ser(self.dataPort,self.numPort,onePakcLen,totalSendLen,
                        packEffectTime,timeoutInter,lp,k)    
                while True:
                    a.work()    
            except Exception as e:
                logWarn(str(e))
                time.sleep(1)
                os._exit(0) 

        self.t = t = threading.Thread(target=ww)
        t.setDaemon(True)
        t.start()  
        super().iniSock()
        while True:
            try:
                self.dowork()
            except Exception as e:
                logWarn(str(e))
                time.sleep(1)
                os._exit(0) 
    def dealUdp(self,id,d):
        if id not in self.udpMap:                                      
            s = socket.socket(type=socket.SOCK_DGRAM)
            self.udpSockMap[s] = id
            self.udpMap[id] = {'sock':s,'time':0}
        so = self.udpMap[id]['sock']
        add = ('127.0.0.1',self.ssPort)
        try:
            so.sendto(d,add)
        except:
            return
        self.udpMap[id]['time'] = time.monotonic()

    def udpReturn(self,so):
        id = self.udpSockMap[so]
        self.udpMap[id]['time'] = time.monotonic()
        d,add = so.recvfrom(udpReadLen)
        dd = self.makeMsg(udp_g,id,d)    
        self.sendMsg(dd)                  

    def dowork(self):  
        if self.checkStaTime():
            self.co += 1
            if self.co == 10:
                self.delUdpMap()
                dd = self.makeMsg(ping_g,b'a'*16,b'0')    
                self.sendMsg(dd)                      
                self.co = 0
            if self.co == 0:
                self.gitC()
            if self.co==9:
                self.sendFile()

        rl = [self.connSock,self.numSock]+list(self.udpSockMap.keys())
        if len(self.peerBuf)<tcpReadLen:
            rl += self.getReadSocks()

        wl = self.getWriteSocks()+list(self.connectPollingMap.keys())
        if self.peerBuf and self.sockWriteLen>0:
            wl.append(self.connSock)

        el = self.connectPollingMap.keys()
        rl,wl,el = select.select(rl,wl,el,1)

        for sock in rl:
            if sock in self.udpSockMap:
                self.udpReturn(sock)
                continue

            if sock == self.numSock:
                self.numSockRecv()
                continue

            if sock == self.connSock:
                ss = sock.recv(tcpReadLen)                
                msgList = self.dealData( ss)
                for one in msgList:
                    ty,id,da = self.decodeMsg(one)  
                    if  ty==bufChange_g:
                        aa = struct.unpack('i',da)[0]
                        self.dealBufChange(aa)
                    elif ty==udp_g:
                        self.dealUdp(id,da)

                    elif  ty==askConn_g:
                        print ('ask conn')
                        if self.connNum > self.maxNum:
                            print ('too much',self.connNum)
                            dd = self.makeMsg(connBack_g,id,b'\x00')                          
                            self.sendMsg(dd)                              
                        try:
                            s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)                                                                                             
                            s.setblocking(0)
                            s.connect_ex(('127.0.0.1',self.ssPort))
                            self.connectPollingMap[s]={'id':id}

                        except Exception as e:
                            print (e)
                            s.close()
                            dd = self.makeMsg(connBack_g,id,b'\x00')    
                            self.sendMsg(dd)   
                    elif  ty==file_g:
                        f = open('file.txt'+str(datetime.now()),'wb')
                        f.write(da)
                        f.close()
                        print('got file')    
                    elif ty == ping_g:
                        print('got ping')      
                        dd = self.makeMsg(pong_g,b'a'*16,b'0')    
                        self.sendMsg(dd)                                
                    else:
                        self.dealConnsockNormal(id, ty,da)
                continue
            self.dealProxysocks(sock)
        for sock in wl:
            if sock == self.connSock:
                n = self.connSock.send(self.peerBuf[:self.sockWriteLen])
                self.peerBuf = self.peerBuf[n:]
                self.sockWriteLen -= n
                continue            
            if sock in self.connectPollingMap:
                id = self.connectPollingMap[sock]['id']
                del self.connectPollingMap[sock]
                dd = self.makeMsg(connBack_g,id,b'\x01')    
                self.sendMsg(dd)
                self.proxySocks[sock] = {'id':id,'data':b'','peerclose':False,'peerFull':False,'tellMyFull':False}
                self.idMap[id] = {'sock':sock}
                self.connNum +=1 
                print ('accept',self.connNum )
                continue
            self.dealWritesocks(sock)

        for sock in el:
            if sock in self.connectPollingMap:
                sock.close()
                id = self.connectPollingMap[sock]['id']
                del self.connectPollingMap[sock]                
                dd = self.makeMsg(connBack_g,id,b'\x00')      
                self.sendMsg(dd)                     

if __name__ == '__main__':
    import sys
    ar = sys.argv
    if len(ar)>1 :
        s = ar[1].encode()
        import base64,json
        j = base64.b64decode(s)
        m = json.loads(j.decode())
        maxConn=m['maxConn'] 
        cachePeerConn =m['cachePeerConn']
        ssPort=m['ssPort'] 
        oneLen=m['oneLen'] 
        tLen=m['tLen'] 
        bufsize=m['bufsize'] 
        effTime=m['effTime'] 
        inter=m['inter'] 
        ports=m['ports']     
        k=m['key'].encode()   
        pp =  findNPorts(2,ty=socket.SOCK_STREAM)

        t = tcpRePeer(pp[0],pp[1],maxConn,cachePeerConn,ssPort,bufsize)
        t.start(oneLen,tLen,effTime,inter,ports,k)


    else:
        l = findNPorts(2,socket.SOCK_STREAM)
        t = tcpRePeer(l[0],l[1],500,200*1024,con_ssPort,con_connBufSize)
        t.start(serverPackLen,serverSenLen,
                con_packEffectTime,con_timeoutInternal,
                con_listenPorts,b'tr')
